﻿#include "xcustombars_p.h"

/*!
 * \brief XCustomBarPrivate::XCustomBarPrivate
 */
XCustomBarPrivate::XCustomBarPrivate()
    : mPenColor(QColor(240, 240, 240)),
      mBrushColor(QColor(240, 240, 240)),
      mTextPosition(None_ValueText),
      mTextOffset(5),
      mTextFont(QFont(QLatin1String("sans serif"), 8)),
      mTextColor(Qt::black)
{

}

/*!
 * \brief XCustomBarPrivate::~XCustomBarPrivate
 */
XCustomBarPrivate::~XCustomBarPrivate()
{

}

/*!
 * \brief XCustomBarPrivate::getDataRange
 * \param range
 * \param data
 */
void XCustomBarPrivate::getDataRange(QCPRange &range, QVector<double> &data)
{
    //获取数据的范围
    QVector<double>::iterator min = std::min_element(std::begin(data), std::end(data));
    QVector<double>::iterator max = std::max_element(std::begin(data), std::end(data));

    range.lower = (*min<0)?(1.2*(*min)):(0);
    range.upper = (*max>0)?(1.2*(*max)):(0);
}

/*!
 * \brief XCustomBar::XCustomBar
 * \param parent
 */
XCustomBar::XCustomBar(QCustomPlot *parent)
    : QCPBars (parent->xAxis, parent->yAxis),
      XCustomBarP(new XCustomBarPrivate)
{
    // set widthType
    this->setWidthType(wtAxisRectRatio);
}

/*!
 * \brief XCustomBar::~XCustomBar
 */
XCustomBar::~XCustomBar()
{
}

/*!
 * \brief XCustomBar::penColor
 * \return
 */
QColor XCustomBar::penColor() const
{
    return XCustomBarP->mPenColor;
}

/*!
 * \brief XCustomBar::brushColor
 * \return
 */
QColor XCustomBar::brushColor() const
{
    return XCustomBarP->mBrushColor;
}

/*!
 * \brief XCustomBar::textPosition
 * \return
 */
ChartValueTextPosition XCustomBar::textPosition() const
{
    return XCustomBarP->mTextPosition;
}

/*!
 * \brief XCustomBar::textOffset
 * \return
 */
double XCustomBar::textOffset() const
{
    return XCustomBarP->mTextOffset;
}

/*!
 * \brief XCustomBar::textFont
 * \return
 */
QFont XCustomBar::textFont() const
{
    return XCustomBarP->mTextFont;
}

/*!
 * \brief XCustomBar::textColor
 * \return
 */
QColor XCustomBar::textColor() const
{
    return XCustomBarP->mTextColor;
}

/*!
 * \brief XCustomBar::setValue
 * \param array
 */
void XCustomBar::setValue(Array &array)
{
    //先判断合法性
    //数组为空直接返回
    if(!array.Count())
        return;

    //判断数组类型
    QString typeName = array.ItemType();
    if ("Integer" != typeName || "Real" != typeName)
        return;

    //将Array转换为QVector
    if("Integer" == typeName)
    {
        for(int i = 0; i < array.Count(); ++i)
        {
            XCustomBarP->data << array.At<Integer>(i).Value();
        }
    }
    else
    {
        for(int i = 0; i < array.Count(); ++i)
        {
            XCustomBarP->data << array.At<Real>(i).Value();
        }
    }

    //获取数据范围
    XCustomBarP->getDataRange(XCustomBarP->dataRange, XCustomBarP->data);

    //刷新主界面范围
    updateDataRange();

    //设置该bar数据
    this->setData(this->keyAxis()->tickVector(), XCustomBarP->data);
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::setPenColor
 * \param color
 */
void XCustomBar::setPenColor(const QColor &color)
{
    XCustomBarP->mPenColor = color;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::setBrushColor
 * \param color
 */
void XCustomBar::setBrushColor(const QColor &color)
{
    XCustomBarP->mBrushColor = color;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::setTextPosition
 * \param position
 */
void XCustomBar::setTextPosition(ChartValueTextPosition position)
{
    XCustomBarP->mTextPosition = position;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::setTextOffset
 * \param offset
 */
void XCustomBar::setTextOffset(double offset)
{
    XCustomBarP->mTextOffset = offset;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::setTextFont
 * \param font
 */
void XCustomBar::setTextFont(const QFont &font)
{
    XCustomBarP->mTextFont = font;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::setTextColor
 * \param color
 */
void XCustomBar::setTextColor(const QColor &color)
{
    XCustomBarP->mTextColor = color;
    this->mParentPlot->replot();
}

/*!
 * \brief XCustomBar::updateDataRange
 */
void XCustomBar::updateDataRange()
{
    QCPRange parentRange = this->mParentPlot->yAxis->range();

    parentRange.lower = qMin(parentRange.lower, XCustomBarP->dataRange.lower);
    parentRange.upper = qMax(parentRange.upper, XCustomBarP->dataRange.upper);

    this->mParentPlot->yAxis->setRange(parentRange);
}

/*!
 * \brief XCustomBar::draw
 * \param painter
 */
void XCustomBar::draw(QCPPainter *painter)
{
    if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
    if (mDataContainer->isEmpty()) return;

    QCPBarsDataContainer::const_iterator constBegin, visibleBegin, visibleEnd;
    // get the first element of data
    constBegin = this->data()->constBegin();
    // get the visible range of data
    getVisibleDataBounds(visibleBegin, visibleEnd);
qDebug() << "----visibleBegin: " << visibleBegin->key << "," << visibleBegin->value;
qDebug() << "----visibleEnd: " << visibleEnd->key << "," << visibleEnd->value;

    // loop over and draw segments of unselected/selected data:
    QList<QCPDataRange> selectedSegments, unselectedSegments, allSegments;
    getDataSegments(selectedSegments, unselectedSegments);
    allSegments << unselectedSegments << selectedSegments;
qDebug() << "----selectedSegments.size(): " << selectedSegments.size();
qDebug() << "----unselectedSegments.size(): " << unselectedSegments.size();
qDebug() << "----allSegments.size(): " << allSegments.size();
    for (int i=0; i < allSegments.size(); ++i)
    {
      bool isSelectedSegment = i >= unselectedSegments.size();
      QCPBarsDataContainer::const_iterator begin = visibleBegin;
      QCPBarsDataContainer::const_iterator end = visibleEnd;
qDebug() << "----begin1: " << begin->key << "," << begin->value;
qDebug() << "----end1: " << end->key << "," << end->value;
qDebug() << "----allSegments.at(i): " << allSegments.at(i);

      mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));

qDebug() << "----begin2: " << begin->key << "," << begin->value;
qDebug() << "----end2: " << end->key << "," << end->value;
      if (begin == end)
        continue;

      // draw in sequence
      for (QCPBarsDataContainer::const_iterator it=begin; it!=end; ++it)
      {
        // check data validity if flag set:
  #ifdef QCUSTOMPLOT_CHECK_DATA
        if (QCP::isInvalidData(it->key, it->value))
          qDebug() << Q_FUNC_INFO << "Data point at" << it->key << "of drawn range invalid." << "Plottable name:" << name();
  #endif
        // draw bar:
        if (isSelectedSegment && mSelectionDecorator)
        {
            mSelectionDecorator->applyBrush(painter);
            mSelectionDecorator->applyPen(painter);
        }
        else
        {
            painter->setPen(XCustomBarP->mPenColor);
            painter->setBrush(XCustomBarP->mBrushColor);
        }
        applyDefaultAntialiasingHint(painter);

        // get the rectangle of the it'th bar and draw the rect
        QRectF barRect = getBarRect(it->key, it->value);
        painter->drawPolygon(barRect);

        // draw valueText
        drawValueText(painter, it, barRect);
      }
    }

    // draw other selection decoration that isn't just line/scatter pens and brushes:
    if (mSelectionDecorator)
        mSelectionDecorator->drawDecoration(painter, selection());
}

/*!
 * \brief XCustomBar::drawValueText
 * \param painter
 * \param it
 * \param barRect
 */
void XCustomBar::drawValueText(QCPPainter *painter, QCPBarsDataContainer::const_iterator it, const QRectF &barRect) const
{
    // draw Text
    painter->setFont(XCustomBarP->mTextFont);
    painter->setPen(XCustomBarP->mTextColor);
    QString text = QString::number(it->value, 'g', 2);   // 取得当前value轴的值，保留两位精度
    QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip | Qt::AlignCenter, text);  // 计算文字所占用的大小

    //如果矩形宽度小于文字宽度则不绘制
    if (textRect.width() > barRect.width())
        return;

    //如果不在顶部,矩形高度小于文字高度则不绘制
    if ((None_ValueText != XCustomBarP->mTextPosition) && (TopOutside_ValueText != XCustomBarP->mTextPosition))
        if (textRect.height() > barRect.height())
            return;

    // set the location of Text
    switch(XCustomBarP->mTextPosition)
    {
    case None_ValueText :
    {
        text.clear();
        textRect.setRect(0, 0, 0, 0);
        break;
    }
    case TopOutside_ValueText :
    {
        textRect.moveBottomLeft(barRect.topLeft() - QPointF(0, XCustomBarP->mTextOffset));
        textRect.setWidth(barRect.width());
        break;
    }
    case TopInside_ValueText :
    {
        textRect.moveBottomLeft(barRect.topLeft()  + QPointF(0, XCustomBarP->mTextOffset + textRect.height()));
        textRect.setWidth(barRect.width());
        break;
    }
    case CenterInside_ValueText :
    {
        textRect.moveBottomLeft(barRect.topLeft()  + QPointF(0, (barRect.height() + textRect.height()) / 2));
        textRect.setWidth(barRect.width());
        break;
    }
    case BottomInside_ValueText :
    {
        textRect.moveBottomLeft(barRect.bottomLeft()  - QPointF(0, XCustomBarP->mTextOffset));
        textRect.setWidth(barRect.width());
        break;
    }
    }
    painter->drawText(textRect, Qt::TextDontClip | Qt::AlignCenter, text); //draw
}


/*!
 * \brief XCustomBarsPrivate::XCustomBarsPrivate
 */
XCustomBarsPrivate::XCustomBarsPrivate()
    : mPlaceStrategy(Adjacent_BarsPlace),
      mBarOffset(1)
{

}

/*!
 * \brief XCustomBarsPrivate::~XCustomBarsPrivate
 */
XCustomBarsPrivate::~XCustomBarsPrivate()
{

}

/*!
 * \brief XCustomBars::XCustomBars
 * \param parent
 */
XCustomBars::XCustomBars(QCustomPlot *parent)
    : QObject(parent),
      barList(new XCustomChartList<XCustomBar*>(parent)),
      customBarsParent(parent),
      mBarsGroup(new QCPBarsGroup(parent)),
      XCustomBarsP(new XCustomBarsPrivate)
{

}

/*!
 * \brief XCustomBars::~XCustomBars
 */
XCustomBars::~XCustomBars()
{
    barList = nullptr;
    customBarsParent = nullptr;
    mBarsGroup = nullptr;
    XCustomBarsP = nullptr;
}


/*!
 * \brief XCustomBars::placeStrategy
 * \return
 */
BarsPlaceStrategy XCustomBars::placeStrategy() const
{
    return XCustomBarsP->mPlaceStrategy;
}

/*!
 * \brief XCustomBars::barOffset
 * \return
 */
double XCustomBars::barOffset() const
{
    return XCustomBarsP->mBarOffset;
}

/*!
 * \brief XCustomBars::setPlaceStrategy
 */
void XCustomBars::setPlaceStrategy(BarsPlaceStrategy strategy)
{
    if(XCustomBarsP->mPlaceStrategy == strategy)
        return;

    XCustomBarsP->mPlaceStrategy = strategy;

    adjustBars();
}

/*!
 * \brief XCustomBars::setBarOffset
 * \param offset
 */
void XCustomBars::setBarOffset(double offset)
{
    if(offset == XCustomBarsP->mBarOffset) return;

    XCustomBarsP->mBarOffset = offset;

    adjustBars();
}

/*!
 * \brief XCustomBars::adjustBars
 */
void XCustomBars::adjustBars()
{
    // adjust according to placeStrategy
    switch (XCustomBarsP->mPlaceStrategy)
    {
    case Adjacent_BarsPlace :
    {
        setAdjacentBars();
        break;
    }
    case Stack_BarsPlace :
    {
        setStackBars();
        break;
    }
    }
}

/*!
 * \brief XCustomBars::setAdjacentBars
 */
void XCustomBars::setAdjacentBars()
{
    if(!mBarsGroup) return;
    if(!mBarsGroup->isEmpty())
        mBarsGroup->clear();

    // join groups one by one
    for(int i = 0; i < barList->chartCount(); ++i)
        mBarsGroup->append(barList->at(i));

    // set the spacing of bars within the group
    mBarsGroup->setSpacingType(QCPBarsGroup::stAxisRectRatio);
    mBarsGroup->setSpacing(XCustomBarsP->mBarOffset);

    customBarsParent->replot();
}

/*!
 * \brief XCustomBars::setStackBars
 */
void XCustomBars::setStackBars()
{
    if(!mBarsGroup) return;
    if(!mBarsGroup->isEmpty())
        mBarsGroup->clear();

    for(int i = 0; i < barList->chartCount(); ++i)
    {
        // adjust textPosition
        if(TopOutside_ValueText == barList->at(i)->textPosition())
            (barList->at(i)->setTextPosition(CenterInside_ValueText));

        barList->at(i)->setStackingGap(XCustomBarsP->mBarOffset);
        barList->at(i)->moveAbove(barList->at(i - 1));
    }

    customBarsParent->replot();
}

